# ESEMPIO LIBRERIA LAYOUT PER ANDROID
#!/usr/bin/python

import logging

#abilita il logging  
logging.basicConfig(filename='fslib.log',level=logging.INFO)
logging.info("--- start test ---")

class ViewError(Exception):
    """Eccezione generica caricata dalla libreria"""
    pass


class View:
    
    """Classe view: top della gerarchia.
    Corrisponde alla classe android.view.View di Java
    Rappresenta un generico elemento view
    """
    xmlns = """xmlns:android="http://schemas.android.com/apk/res/android" """
    xml_header = """<?xml version="1.0" encoding="utf-8"?>\n"""
    template = """<%(name)s %(xmlns)s
    android:id="@+id/%(id)s"
    android:layout_width="%(width)s"
    android:layout_height="%(height)s"\n%(xml_additional_attrs)s/>"""
    
    def __init__(self,sid,height,width,xml_attrs):
        self._id = sid               # id di questo elemento: privato!
        self.height = height
        self.width = width
        self.xml_attrs = xml_attrs   # attributi xml addizionali        
        self.droid = None            # elemento Android view
        self.name = "View"
        self.listeners = {}                 
     
    def __str__(self):
        """Ritorna una semplice (non-ricorsiva) rappresentazione xml"""
        return View.xml(self,False) 
        
    def xml(self,root=False):
        """Rappresentazione xml della View"""
        xmlns = View.xmlns if root else ""
        s = View.template % {
             "name":self.name,
             "xmlns":xmlns,
             "id":self._id,
             "height": self.height,
             "width":self.width,
             "xml_additional_attrs":self.translate_attrs()
             }
        return s
    
    def show(self,droid):
        """Apertura del droid"""
        self.droid = droid        # salva per future modifiche e per la chiusura
        s = self.xml(True)        # prende la xml root representation
        s = View.xml_header + s
        logging.info("[fullShowXML] " + s)
        out = droid.fullShow(s)
        logging.info("[fullShow] " + str(out))
        self.displayed_ids = self.contained_ids() # record contenente l'id al tempo dell'apertura
        
    def dismiss(self):
        """Chiusura del droid"""
        if self.droid is not None:
            out = self.droid.fullDismiss()
            logging.info("[fullDismiss] " + str(out))
            self.droid = None        # L'elemento View non e' piu' usato
        else:
            raise ViewError("[dismiss] View not currently displayed")
    
    def setXmlAttribute(self,attr,value):
        """Setta un attributo nella rappresentazione xml"""
        self.xml_attrs[attr]=value  

    def setMargins(self,l,r,t,b,units):
        """Setta i margini sinistro/destro/alto/basso nella rappresentazione xml"""
        self.xml_attrs["layout_marginLeft"]=str(l)+units
        self.xml_attrs["layout_marginRight"]=str(r)+units
        self.xml_attrs["layout_marginTop"]=str(t)+units
        self.xml_attrs["layout_marginBottom"]=str(b)+units
    
    def contains(self,some_id):
        """Vero se self contiene una view con id=some_id"""
        return self._id == some_id
    
    def contained_ids(self):
        """Ritorna l'hash id:View di tutto cio' che e' contenuto nell'elemento View"""
        return {self._id:self}
        
    def setProperty(self,viewid,attr,value):
        """Cambia gli attributi dell'elemento self"""
        if self.droid is None:
            raise ViewError("[setProperty] View %s not currently displayed" % self._id)
        if viewid in self.displayed_ids.keys():
            self.displayed_ids[viewid]._setProperty(self.droid,attr,value)
        else:
            raise ViewError("[setProperty] Unknown id %s in View %s" % (viewid,self._id))
        
    def _setProperty(self,some_droid,attr,value):
        """Cambia gli attributi della view corrente"""
        out = some_droid.fullSetProperty(self._id,attr,value)
        logging.info("[_setProperty] " + str(out))
        if out.error is not None:
            raise ViewError("[_setProperty] "+str(out))
        
    def setList(self,viewid,itemlist):
        """Setta la lista associata all'elemento self"""
        if self.droid is None:
            raise ViewError("[setList] View %s not currently displayed" % self._id)
        if viewid in self.displayed_ids.keys():
            self.displayed_ids[viewid]._setList(self.droid,itemlist)
        else:
            raise ViewError("[hide] Unknown id %s in View %s" % (viewid,self._id))
        
    def hide(self,viewid):
        """Nasconde l'elemento self"""
        if self.droid is None:
            raise ViewError("[hide] View %s not currently displayed" % self._id)
        if viewid in self.displayed_ids.keys():
            self.displayed_ids[viewid]._hide(self.droid)
        else:
            raise ViewError("[hide] Unknown id %s in View %s" % (viewid,self._id))
        
    def highlight(self,viewid,color):
        """Modifica l'elemento self"""
        if self.droid is None:
            raise ViewError("[highlight] View %s not currently modified" % self._id)
        if viewid in self.displayed_ids.keys():
            self.displayed_ids[viewid]._highlight(self.droid,color)
        else:
            raise ViewError("[highlight] Unknown id %s in View %s" % (viewid,self._id))
       
    def return_highlight(self,viewid):
        """Modifica l'elemento self"""
        if self.droid is None:
            raise ViewError("[return_highlight] View %s not currently re-modified" % self._id)
        if viewid in self.displayed_ids.keys():
            self.displayed_ids[viewid]._return_highlight(self.droid)
        else:
            raise ViewError("[return_highlight] Unknown id %s in View %s" % (viewid,self._id))
        
    def queryDetail(self,viewid):
        """Preleva le informazioni dettagliate dell'elemento self"""
        if self.droid is None:
            raise ViewError("[queryDetail] View %s not currently displayed" % self._id)
        if viewid in self.displayed_ids.keys():
            self.displayed_ids[viewid]._queryDetail(self.droid)
        else:
            raise ViewError("[queryDetail] Unknown id %s in View %s" % (viewid,self._id))
        
    def _queryDetail(self,some_droid):
        """Preleva le informazioni dettagliate del View corrente"""
        r = some_droid.fullQueryDetail(self._id)
        logging.info("[_queryDetail] " + str(r))
        if r.error is not None:
            raise ViewError("[_queryDetail] "+str(r))
        return r.result
    
    def getItemPosition(self,viewid):
        """Preleva la posizione dell'elemento self"""
        if self.droid is None:
            raise ViewError("[getItemPosition] View %s not currently displayed" % self._id)
        if viewid in self.displayed_ids.keys():
            return self.displayed_ids[viewid]._getItemPosition(self.droid)
            #self.displayed_ids[viewid]._queryDetail(self.droid).result["selectedItemPosition"]
        else:
            raise ViewError("[getItemPosition] Unknown id %s in View %s" % (viewid,self._id))
        
    def setListener(self,event,action):
        """Associa al dizionario listeners uno specifico evento"""
        self.listeners[event] = action
        
    def translate_attrs(self):
        """Costruisci un linea "android:attr=value" per ogni item in xml_attr"""
        attrs = ""
        for (a,v) in self.xml_attrs.items():
            attrs += """    android:%s="%s"\n""" % (a,v)
        return attrs
    

class TextView(View):
    
    def __init__(self, sid, height, width, xml_attrs):
        View.__init__(self,sid, height, width, xml_attrs)
        self.name="TextView"
        
    def xml(self,root=False):
        if "text" not in self.xml_attrs.keys():
            raise ViewError('[xml] Missing "text" attribute in TextView')
        return View.xml(self,root)
    
    def _hide(self,some_droid):
        """Nasconde lo status corrente"""
        self._setProperty(some_droid,"text","")
        self._setProperty(some_droid,"background","0x0000") #nero trasparente
        
    def _unhide(self,some_droid):
        """Ripristina lo status corrente"""
        self._setProperty(some_droid,"text",self.xml_attrs["text"])
        self._setProperty(some_droid,"background", self.xml_attrs["background"])
        
    def _highlight(self,some_droid,color):
        """Cambia il background all'elemento corrente"""
        self._setProperty(some_droid, "background", color)
        
        
    def _return_highlight(self,some_droid):
        """Ripristina il background corrente"""
        self._setProperty(some_droid,"background", self.xml_attrs["background"])
        
        
class Button(TextView):
    
    def __init__(self, sid, height, width, xml_attrs):
        View.__init__(self, sid, height, width, xml_attrs)
        self.name="Button"
        

class EditText(TextView):
    
    def __init__(self, sid, height, width, xml_attrs):
        View.__init__(self, sid, height, width, xml_attrs)
        self.name="EditText"
        

class ViewGroup(View):
    
    #La classe ViewGroup e le sue sottoclassi possono avere figli di tipo View
    
    template = """<%(name)s %(xmlns)s
    android:id="@+id/%(id)s"
    android:layout_width="%(width)s"
    android:layout_height="%(height)s"\n%(xml_additional_attrs)s>
    %(items)s</%(name)s>"""
    
    def __init__(self, sid, height, width, xml_attrs):
        View.__init__(self,sid, height, width, xml_attrs)
        self.children = []
        self.name = "ViewGroup"
        
    def xml(self,root=False):
        """Rappresentazione xml della ViewGroup"""
        xmlns = View.xmlns if root else ""
        xml_items = ""
        for i in self.children:
            xml_items += i.xml() + "\n"    
        s = ViewGroup.template % {
             "name":self.name,
             "xmlns":xmlns,
             "id":self._id,
             "height": self.height,
             "width":self.width,
             "xml_additional_attrs":self.translate_attrs(),
             "items":xml_items
            }
        return s    
        
    def contains(self,some_id):
        """Vero se self contiene una view con id=some_id"""
        if self._id == some_id: return True
        for v in self.children:
            if v.contains(some_id): return True
        return False
    
    def contained_ids(self):
        """Ritorna l'hash id->View di tutti gli elementi View contenuti"""
        tmp = {self._id:self}
        for v in self.children:
            t = v.contained_ids() # hash per i sottoelementi v
            for (i,w) in t.items():
                if i in tmp.keys():
                    raise ViewError('Duplicate id "%s" in View "%s"' % (i,self._id))
                tmp[i] = w
        return tmp
    
    def addView(self,v):
        """Aggiunge la nuova View alla fine della lista dei figli"""
        self.children.append(v)

    def insertView(self,i,v):
        """Aggiunge la nuova View in posizione i della lista dei figli"""
        self.children.insert(i,v)
        

class LinearLayout(ViewGroup):
    
    def __init__(self, sid, height, width, xml_attrs):            
        ViewGroup.__init__(self,sid, height, width, xml_attrs)
        self.name = "LinearLayout"
     
    def xml(self,root=False):
        if "orientation" not in self.xml_attrs.keys():
            raise ViewError('[xml] Missing "orientation" attribute in LinearLayout')
        return ViewGroup.xml(self,root)
    

class AdapterView(ViewGroup):

    def __init__(self, sid, height, width, xml_attrs):            
        ViewGroup.__init__(self,sid, height, width, xml_attrs)
        self.name = "AdapterView"

    def _setList(self,some_droid, some_list):
        """Setta la lista degli adapter correnti"""
        out = some_droid.fullSetList(self._id,some_list)
        logging.info("[_setList] " + str(out))
        if out.error is not None:
            raise ViewError("[_setList] "+str(out))

 
class ListView(AdapterView):
    
    def __init__(self, sid, height, width, xml_attrs):            
        AdapterView.__init__(self,sid, height, width, xml_attrs)
        self.name = "ListView"
 

class Spinner(AdapterView):
 
    def __init__(self, sid, height, width, xml_attrs):            
        AdapterView.__init__(self,sid, height, width, xml_attrs)
        self.name = "Spinner"
 
    def _getItemPosition(self,some_droid):
        r = some_droid.fullQueryDetail(self._id)
        logging.info("[_getItemPosition] " + str(r))
        if r.error is not None:
            raise ViewError("[_getItemPosition] "+str(r))
        return int(r.result["selectedItemPosition"])